<template>
  <div id="my-three"></div>
</template>

<script setup>
import { ref, shallowRef } from 'vue'
import * as THREE from 'three'
import { GridHelper } from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
import { onMounted, onUnmounted } from 'vue'

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass';
import GUI from 'lil-gui'//调色板
import debounce from 'lodash.debounce';;//防抖函数

const computerColor = ref('#3A3C3B')
const computerSetup = ref({
  color: '#3A3C3B', // 黄色
  metalness: 0.9,  // 高金属度
  roughness: 0.3,  // 中等粗糙度
  envMapIntensity: 2, // 环境贴图强度
  emissive: '#444444',//微弱自发光
  emissiveIntensity: 0.5
})

// 创建三维场景
const scene = new THREE.Scene();


///////////////////////////////////////////////////////////////////////////////////////////添加圆柱体房间代码，无需房间注释即可,同时将'无需房间的代码'的代码打开
// 在场景创建后添加圆柱体房间
const roomHeight = 500; // 房间高度
const roomRadius = 500; // 半径与地面相同(地面尺寸1000，半径500)
//创建圆柱体墙壁(去掉顶部和底部)
const roomGeometry = new THREE.CylinderGeometry(
  roomRadius,
  roomRadius,
  roomHeight,
  64,
  1,
  true, // 不渲染底面
  false // 不渲染顶面
);

// 添加天花板
const ceilingGeometry = new THREE.CircleGeometry(roomRadius, 64);
const ceilingMaterial = new THREE.MeshStandardMaterial({
  color: '#ffffff',
  side: THREE.DoubleSide,
  transparent: true,
  opacity: 1,    // 提高透明度
  roughness: 0.1,  // 表面光滑度
  metalness: 0.5   // 轻微金属感
});

const roomMaterial = new THREE.MeshStandardMaterial({
  color: '#ffffff',
  side: THREE.DoubleSide, // 双面渲染
  transparent: true,
  // opacity: 0.3, // 半透明效果
  roughness: 0.7,
  metalness: 0.1
});


const room = new THREE.Mesh(roomGeometry, roomMaterial);
room.position.y = roomHeight / 2; // 将圆柱体抬高到地面以上
room.receiveShadow = true;
scene.add(room);


//天花板顶部实现
const ceiling = new THREE.Mesh(ceilingGeometry, ceilingMaterial);
ceiling.rotation.x = Math.PI / 2; // 旋转到水平
ceiling.position.y = roomHeight; // 放到圆柱体顶部
scene.add(ceiling);
////////////////////////////////////////////////////////////////////////////////////////////



// 添加光滑灰色的地面
const groundGeometry = new THREE.PlaneGeometry(1000, 1000); // 地面大小
const groundMaterial = new THREE.MeshStandardMaterial({
  color: '#E4E4E4', // 灰色
  roughness: 0.1,  // 表面光滑度
  metalness: 0.5   // 轻微金属感
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2; // 旋转使平面水平
ground.receiveShadow = true; // 允许接收阴影
scene.add(ground);




// 创建透视相机
let width = window.innerWidth, height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000);
camera.position.set(50, 10, 100); // 设置相机位置
camera.lookAt(0, 10, 0);; // 设置相机朝向


// ==================== 灯光设置 ====================
// 添加光源
const ambient = new THREE.AmbientLight('#ffffff', 1.5); // 环境光
scene.add(ambient);

// 1. 主灯光（Z轴方向）
const zAxisLight = new THREE.DirectionalLight(
  '#ffffff', // 灯光颜色（白色）
  2          // 光照强度
);
zAxisLight.position.set(0, camera.position.y, 150); // 灯光位置
zAxisLight.castShadow = true; // 启用阴影
zAxisLight.shadow.mapSize.width = 2048; // 阴影贴图宽度
zAxisLight.shadow.mapSize.height = 2048; // 阴影贴图高度
scene.add(zAxisLight);


// 2. 背光
const backLight = new THREE.DirectionalLight('#ffffff', 30); // 更强的白光
backLight.position.set(100, 200, -100); // 位于模型右后上方
backLight.castShadow = true;
backLight.shadow.mapSize.width = 1024; // 中等质量阴影
backLight.shadow.mapSize.height = 1024;
scene.add(backLight); // 将灯光附加到模型上

// // 创建一个来自正上方的光源
// const topLight = new THREE.DirectionalLight('#ffffff', 1.5); // 定向光，模拟阳光
// topLight.position.set(0, 100, 0); // 设置光源位置为正上方
// scene.add(topLight);

// 添加来自右上方的强灯光
// const rightTopLight = new THREE.DirectionalLight('#ffffff', 8); // 更强的灯光
// rightTopLight.position.set(400, 200, -100); // 右上方位置
// rightTopLight.castShadow = true; // 允许投射阴影

// // 设置阴影参数
// rightTopLight.shadow.mapSize.width = 2048;
// rightTopLight.shadow.mapSize.height = 2048;
// scene.add(rightTopLight);



// 创建WebGL渲染器
const renderer = new THREE.WebGLRenderer({
  antialias: true,
  powerPreference: "high-performance" // 启用高性能模式
});
renderer.setSize(width, height);
// renderer.shadowMap.enabled = true; // 启用阴影渲染
renderer.physicallyCorrectLights = true; // 物理正确光照
renderer.outputEncoding = THREE.SRGBColorSpace; // 使用sRGB色彩空间
renderer.toneMapping = THREE.ACESFilmicToneMapping; // 电影级色调映射
renderer.toneMappingExposure = 1.2; // 增加曝光值
renderer.render(scene, camera);

// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 10, 0); // 控制中心点
controls.minPolarAngle = 0; // 限制最小仰角(避免过度俯视)
controls.maxPolarAngle = 80 / 360 * 2 * Math.PI; // 限制最大俯角(避免过度仰视,不允许查看建模底部，如需要可查看，可为0慢慢调试)
controls.minDistance = 30;// 最小缩放距离
controls.maxDistance = 200;// 最大缩放距离
// 启用阻尼效果（更流畅的操作）
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.addEventListener('change', () => {
  renderer.render(scene, camera); // 每次控制变化时重新渲染
});


// 创建坐标轴帮助器
// const axesHelper = new THREE.AxesHelper(100);
// scene.add(axesHelper);

// 添加环境贴图
const envTexture = new THREE.CubeTextureLoader()
  .load(['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg']);
envTexture.encoding = THREE.sRGBEncoding;
scene.environment = envTexture; // 关键！这会提供反射信息


// 初始化后期处理器
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));

// 添加辉光效果
const bloomPass = new UnrealBloomPass(
  new THREE.Vector2(width, height),
  1.5, // 强度
  0.4, // 半径
  0.85  // 阈值
);
composer.addPass(bloomPass);



// 窗口大小变化处理函数
const handleResize = () => {
  // 更新窗口尺寸
  width = window.innerWidth;
  height = window.innerHeight;

  // 更新相机参数
  camera.aspect = width / height;
  camera.updateProjectionMatrix();

  // 更新渲染器尺寸
  renderer.setSize(width, height);

  // 重新渲染场景
  renderer.render(scene, camera);
};

// 添加动画循环函数---用于解决刷新之后自定义模型不显示的问题
function animate() {
  requestAnimationFrame(animate);
  controls.update(); // 只在需要时更新控制器
  composer.render();
  renderer.render(scene, camera);
}


// 防抖加载模型（延迟 500ms，确保颜色稳定后再执行）否则会卡
const debouncedLoadModel = debounce(() => {
  loadModel();
}, 500);


//调色器
const initGUI = () => {
  let objcolor = {
    bodyColor: "#3A3C3B"
  }
  let gui = new GUI()
  gui.addColor(objcolor, 'bodyColor').name('电脑颜色').onChange(value => {
    computerSetup.value.color = value
    debouncedLoadModel()
  })
}


// 定义事件处理函数引用
const onClickHandler = ref(null)//用于接收点击事件
const targetMesh = shallowRef(null); //用于接收组件----使用shallowRef定义是因为使用ref定义会被转为Proxy(Mesh)对象影响了贴图操作
const textureLoaded = ref(false);//用于接收屏幕贴图状态
const currentModel = ref(null) // 用于跟踪当前模型
/**
 * 加载并设置3D模型的函数
 */
const loadModel = () => {
  // 清理旧模型
  if (currentModel.value) {
    scene.remove(currentModel.value)
    currentModel.value.traverse(child => {
      if (child.isMesh) child.material.dispose()
    })
    currentModel.value = null
    targetMesh.value = null
  }

  // 移除旧事件监听
  if (onClickHandler.value) {
    window.removeEventListener('click', onClickHandler.value)
    onClickHandler.value = null
  }

  // 创建FBX加载器实例
  const loader = new FBXLoader();
  loader.load('/models/fandizuo.fbx', (object) => {
    // 将加载的模型添加到场景中
    currentModel.value = object//保存模型
    scene.add(object);

    // ==================== 模型位置和尺寸设置 ====================
    // 计算模型的包围盒（Bounding Box）
    const bbox = new THREE.Box3().setFromObject(object);
    // 获取模型高度（Y轴方向）
    const modelHeight = bbox.max.y - bbox.min.y;

    // 设置模型旋转（绕Y轴25.6度）
    object.rotation.y = THREE.MathUtils.degToRad(25.6); // 转换为弧度

    // 设置模型位置（Y轴位置为模型高度/1.6）
    object.position.set(0, modelHeight / 1.6, 0);



    // ==================== 材质管理 ====================
    // 原始材质设置（用于恢复初始状态）
    const originalMaterialSettings = {
      color: '#3A3C3B',  // 深灰色
      metalness: 0.9,    // 高金属质感
      roughness: 0,      // 完全光滑
      envMapIntensity: 2 // 环境贴图反射强度
    };

    // 遍历模型所有子对象
    object.traverse((child) => {
      if (child.isMesh) { // 只处理网格对象
        if (child.name === '凸台_拉伸34') {
          targetMesh.value = child; // 保存目标组件引用
          // 应用初始材质设置
          child.material = new THREE.MeshStandardMaterial(originalMaterialSettings);
        } else {
          // 其他组件使用统一设置
          child.material = new THREE.MeshStandardMaterial(computerSetup.value);
          child.castShadow = true; // 启用阴影
        }
      }
    });

    // ==================== 贴图操作函数 ====================
    /**
     * 应用贴图到目标网格
     * @mesh - 目标网格对象
     */
    const applyTexture = (mesh) => {
      console.log('进行贴图')
      if (textureLoaded.value) return; // 防止重复加载

      // 创建纹理加载器
      const textureLoader = new THREE.TextureLoader();

      // 加载贴图文件
      textureLoader.load('/textures/91232338-98c4-41fb-bd6a-2ad4715abdf6.jpg',
        (texture) => { // 加载成功回调
          texture.encoding = THREE.sRGBEncoding; // 设置正确的颜色空间

          // 创建带贴图的新材质（扩展原始设置）
          mesh.material = new THREE.MeshStandardMaterial({
            ...originalMaterialSettings, // 保留原始设置
            map: texture,              // 基础颜色贴图
            metalness: 0.5,            // 适当降低金属度
            roughness: 0.3,            // 稍微增加粗糙度
            emissiveMap: texture,      // 自发光贴图
            emissiveIntensity: 2.0,    // 自发光强度
            emissive: 0xffffff,       // 自发光颜色（白色）
            envMapIntensity: 3         // 增强环境反射
          });

          textureLoaded.value = true; // 更新状态
          console.log('贴图已应用');
        },
        undefined, // 加载进度回调（未使用）
        (error) => { // 错误处理
          console.error('纹理加载失败:', error);
        }
      );
    };

    /**
     * 移除目标网格的贴图
     * @mesh - 目标网格对象
     */
    const removeTexture = (mesh) => {
      if (!textureLoaded.value) return; // 防止重复移除

      // 重新创建原始材质（完全重置）
      mesh.material = new THREE.MeshStandardMaterial(originalMaterialSettings);
      textureLoaded.value = false; // 更新状态
      console.log('贴图已移除');
    };

    // ==================== 点击事件处理 ====================
    /**
     * 点击事件处理函数
     * @event - 鼠标事件对象
     */
    onClickHandler.value = (event) => {
      console.log(object)
      // 将鼠标坐标转换为标准化设备坐标（-1到1）
      const mouse = new THREE.Vector2(
        (event.clientX / window.innerWidth) * 2 - 1,
        -(event.clientY / window.innerHeight) * 2 + 1
      );

      // 创建射线投射器
      const raycaster = new THREE.Raycaster();
      raycaster.setFromCamera(mouse, camera); // 从相机发出射线

      // 检测与模型的交点
      const intersects = raycaster.intersectObject(object, true);
      if (intersects.length > 0) {
        const clickedObject = intersects[0].object;
        // 检查是否点击了目标组件或其子对象
        if (clickedObject === targetMesh.value || clickedObject.parent === targetMesh.value) {
          // 根据当前状态切换贴图
          if (textureLoaded.value) {
            removeTexture(targetMesh.value);
          } else {
            applyTexture(targetMesh.value);
          }
        }
      }
    };

    // 添加点击事件监听
    window.addEventListener('click', onClickHandler.value);

    // 刷新回显操作
    animate();

  },
    undefined, // 加载进度回调（未使用）
    (error) => { // 模型加载错误处理
      console.error('FBX模型加载错误:', error);
    });
};

// 在组件卸载时执行清理
onUnmounted(() => {
  // 移除事件监听
  window.removeEventListener('click', onClickHandler.value)
  // 清理材质资源
  if (targetMesh.value && targetMesh.value.material) {
    targetMesh.value.material.dispose();
  }
});

onMounted(() => {
  loadModel();
  initGUI()
  document.getElementById('my-three')?.appendChild(renderer.domElement);
  // 添加窗口大小变化监听
  window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
  // 组件卸载时移除监听器
  window.removeEventListener('resize', handleResize);
});
</script>

<style>
body {
  margin: 0;
  padding: 0;
}

.my-three {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
</style>